package com.gitee.btssplib

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.util.Log
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.FlowableOnSubscribe
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.*

/**
 * 蓝牙连接器，负责连接蓝牙.
 * @author John
 * @version 1.0
 */
class BTConnector(var connectListener: ConnectListener? = null) {

    companion object {
        const val TAG = "BTConnector"
        private val UUID_OTHER_DEVICE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
    }

    enum class State {
        IDLE, // 未连接
        CONNECT_ERROR, // 连接错误
        CONNECTING, // 正在连接
        CONNECTED // 已连接
    }

    private var bluetoothAdapter: BluetoothAdapter = BluetoothAdapter.getDefaultAdapter() // 蓝牙设配器
    private var connectThread: Disposable? = null // 连接线程
    var bluetoothSocket: BluetoothSocket? = null // 已连接的socket
    var exception: Throwable? = null // 异常
    var state: State = State.IDLE // 连接状态
    var bluetoothDevice: BluetoothDevice? = null // 连接的蓝牙设备
        set(value) {
            field = value
            Log.d(TAG, "设置新的蓝牙设备")
        }

    @Synchronized
    fun connect(device: BluetoothDevice) {
        when (state) {
            State.IDLE -> {
                bluetoothDevice = device
                connectBluetoothDevice()
            }
            else -> {
                stop()
                bluetoothDevice = device
                connectBluetoothDevice()
            }
        }
    }

    private fun connectBluetoothDevice() {
        val device = bluetoothDevice ?: throw RuntimeException("蓝牙设备为空")

        connectThread = Flowable.create(FlowableOnSubscribe<BluetoothSocket> { emitter ->
            try {
                Log.d(TAG, "连接蓝牙设备前，取消搜索蓝牙设备")
                bluetoothAdapter.cancelDiscovery()
                Log.d(TAG, "正在连接蓝牙设备")
                val bluetoothSocket = device.createRfcommSocketToServiceRecord(UUID_OTHER_DEVICE)
                bluetoothSocket.connect()
                Log.d(TAG, "连接成功")
                emitter.onNext(bluetoothSocket)
            } catch (e: Exception) {
                e.printStackTrace()
                emitter.tryOnError(e)
            } finally {
                emitter.onComplete()
            }
        }, BackpressureStrategy.BUFFER)
                .retryWhen(RetryFunction())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ result ->
                    Log.d(TAG, "连接蓝牙设备结果：$result")
                    bluetoothSocket = result
                    state = State.CONNECTED
                    connectListener?.onConnected()
                }, { error ->
                    Log.d(TAG, "连接蓝牙设备发生异常")
                    state = State.CONNECT_ERROR
                    exception = error
                    connectListener?.onError(error)
                    error.printStackTrace()
                }, {
                    Log.d(TAG, "连接蓝牙设备完成")
                }, { subscription ->
                    subscription.request(Long.MAX_VALUE)
                    Log.d(TAG, "开始连接蓝牙设备")
                    state = State.CONNECTING
                    connectListener?.onStart()
                })
    }

    @Synchronized
    fun stop() {
        closeConnectThread()
        closeBluetoothSocket()
        state = State.IDLE
    }

    private fun closeConnectThread() {
        try {
            connectThread?.dispose()
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            connectThread = null
        }
    }

    private fun closeBluetoothSocket() {
        try {
            bluetoothSocket?.close()
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            bluetoothSocket = null
            state = State.IDLE
        }
    }

    @Synchronized
    fun isConnected(): Boolean {
        return state == State.CONNECTED
    }

    @Synchronized
    fun isConnecting(): Boolean {
        return state == State.CONNECTING
    }

    @Synchronized
    fun reconnect() {
        if (state != State.CONNECTING) {
            connectBluetoothDevice()
        }
    }
}